// Tencent is pleased to support the open source community by making
// 蓝鲸智云 - 监控平台/日志平台 (BlueKing - Monitor/Log) available.
// Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://opensource.org/licenses/MIT
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//

package servicediscoverer

import (
	"github.com/TencentBlueKing/bkmonitor-kits/logger"
	"github.com/mitchellh/mapstructure"
	"go.opentelemetry.io/collector/model/pdata"

	"github.com/TencentBlueKing/bk-collector/confengine"
	"github.com/TencentBlueKing/bk-collector/define"
	"github.com/TencentBlueKing/bk-collector/processor"
)

const (
	ProcessorName string = "service_discoverer"
)

func init() {
	processor.Register(ProcessorName, NewFactory)
}

func NewFactory(conf map[string]interface{}, customized []processor.SubConfigProcessor) (processor.Processor, error) {
	return newFactory(conf, customized)
}

func newFactory(conf map[string]interface{}, customized []processor.SubConfigProcessor) (*serviceDiscoverer, error) {
	configs := confengine.NewTierConfig()

	c := &Config{}
	if err := mapstructure.Decode(conf, c); err != nil {
		return nil, err
	}
	c.Setup()
	configs.SetGlobal(NewConfigHandler(c))

	for _, custom := range customized {
		var cfg Config
		if err := mapstructure.Decode(custom.Config.Config, &cfg); err != nil {
			logger.Errorf("failed to decode config: %v", err)
			continue
		}
		configs.Set(custom.Token, custom.Type, custom.ID, NewConfigHandler(c))
	}

	return &serviceDiscoverer{
		CommonProcessor: processor.NewCommonProcessor(conf, customized),
		fetcher:         processor.NewSpanDimensionFetcher(),
		matcher:         NewMatcher(),
		configs:         configs,
	}, nil
}

type serviceDiscoverer struct {
	processor.CommonProcessor
	matcher Matcher
	fetcher processor.SpanDimensionFetcher
	configs *confengine.TierConfig // type: *ConfigHandler
}

func (p serviceDiscoverer) Name() string    { return ProcessorName }
func (p serviceDiscoverer) IsDerived() bool { return false }

func (p serviceDiscoverer) Process(record *define.Record) (*define.Record, error) {
	switch record.RecordType {
	case define.RecordTraces:
		p.processTraces(record)
		return nil, nil
	}

	return nil, nil
}

func (p serviceDiscoverer) processTraces(record *define.Record) {
	pdTraces := record.Data.(pdata.Traces)
	resourceSpans := pdTraces.ResourceSpans()
	ch := p.configs.GetByToken(record.Token.Original).(*ConfigHandler)

	for i := 0; i < resourceSpans.Len(); i++ {
		instSpans := resourceSpans.At(i).InstrumentationLibrarySpans()
		for j := 0; j < instSpans.Len(); j++ {
			spans := instSpans.At(j).Spans()
			for k := 0; k < spans.Len(); k++ {
				span := spans.At(k)
				rules := ch.Get(span.Kind().String())
			loop:
				for _, rule := range rules {
					df, pk := processor.DecodeDimensionFrom(rule.PredicateKey)
					switch df {
					// TODO(mando): 目前 predicateKey 暂时只支持 attributes 后续可能会扩展
					case processor.DimensionFromAttribute:
						if s := p.fetcher.FetchAttribute(span, pk); s == "" {
							continue
						}
						attr := rule.AttributeValue()
						if attr == "" {
							continue
						}
						val := p.fetcher.FetchAttribute(span, attr)
						if val == "" {
							continue
						}

						switch rule.SubsetType() {
						case SubsetTypeDomain:
							rule.MatchDomain(val)
						default:
							continue
						}

						matched := p.matcher.Match(span, rule.Regexp(), val, rule.groups)
						if !matched {
							continue
						}

						break loop
					}
				}
			}
		}
	}
}
